package com.atguigu.gmall.order.service.impl;

import com.atguigu.gmall.cart.client.CartFeignClient;
import com.atguigu.gmall.cart.model.CartInfo;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.rabbit.config.MqConst;
import com.atguigu.gmall.common.rabbit.service.RabbitService;
import com.atguigu.gmall.common.util.HttpClientUtil;
import com.atguigu.gmall.enums.model.OrderStatus;
import com.atguigu.gmall.enums.model.ProcessStatus;
import com.atguigu.gmall.order.mapper.OrderInfoMapper;
import com.atguigu.gmall.order.model.OrderDetail;
import com.atguigu.gmall.order.model.OrderInfo;
import com.atguigu.gmall.order.service.OrderDetailService;
import com.atguigu.gmall.order.service.OrderInfoService;
import com.atguigu.gmall.product.client.ProductFeignClient;
import com.atguigu.gmall.user.client.UserFeignClient;
import com.atguigu.gmall.user.model.UserAddress;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
 * 订单表 订单表 业务实现类
 *
 * @author lan
 * @since 2023-05-06
 */
@Service

public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Autowired
    private CartFeignClient cartFeignClient;
    @Autowired
    private UserFeignClient userFeignClient;
    @Autowired
    private OrderDetailService orderDetailService;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private ProductFeignClient productFeignClient;
    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;
    @Autowired
    private RabbitService rabbitService;

    @Value("${ware.url}")
    private String wareUrl;
    /**
     * 汇总渲染订单确认页面所选的数据
     *
     * @param valueOf@return
     */
    @Override
    public Map<String, Object> trade(Long userId) {
        HashMap<String, Object> mapResult = new HashMap<>();
        //调用用户微服务获取收件地址列表
        List<UserAddress> userAddresses = userFeignClient.getUserAddressListByUserId(userId);
        if (!CollectionUtils.isEmpty(userAddresses)){
            mapResult.put("userAddressList",userAddresses);
        }
        //调用购物车微服务获取需要结算的商品
        List<CartInfo> cartInfoList = cartFeignClient.getCartCheckedList(userId);
        //将级和反省从购物车类型转成订单明细类型
        if (!CollectionUtils.isEmpty(cartInfoList)) {
            List<OrderDetail> orderDetailList = cartInfoList.stream().map(cartInfo -> {

                OrderDetail orderDetail = new OrderDetail();
                BeanUtils.copyProperties(cartInfo, orderDetail);
                //单独设置价格属性
                orderDetail.setOrderPrice(cartInfo.getSkuPrice());
                return orderDetail;
            }).collect(Collectors.toList());
            mapResult.put("detailArrayList",orderDetailList);
            //3.封装${totalNum}订单商品数量
            mapResult.put("totalNum",orderDetailList.size());
            //4.订单总金额
            OrderInfo orderInfo = new OrderInfo();
            orderInfo.setOrderDetailList(orderDetailList);
            orderInfo.sumTotalAmount();
            mapResult.put("totalAmount",orderInfo.getTotalAmount());
        }
        //5.生成一个流水号
        String tradeNo = this.generateTradeNo(userId.toString());
        mapResult.put("tradeNo",tradeNo);
        return mapResult;
    }

    /**
     * 提交订单
     *
     * @param orderInfo
     * @param tradeNo
     * @return
     */
    @Override
    public Long submitOrder(OrderInfo orderInfo, String tradeNo) {
//        //1.避免用户无意使用浏览器回退,导致重复提交订单
//        Boolean aBoolean = checkTradeNo(orderInfo.getUserId().toString(), tradeNo);
//        if (!aBoolean){
//            throw  new RuntimeException("别重复提交订单,!尼龙啊");
//        }
//        //1.1保存以后,,记得把流水号删除
//        deleteTradeNo(orderInfo.getUserId().toString());
        //采用lua脚本保证判断流水号和删除流水号原子性
        String luaScriptText = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                "then\n" +
                "    return redis.call(\"del\",KEYS[1])\n" +
                "else\n" +
                "    return 0\n" +
                "end";
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(luaScriptText);
        redisScript.setResultType(Long.class);
        String tradeKey="tradeNo:"+orderInfo.getUserId();
        Long flag =(Long) redisTemplate.execute(redisScript, Arrays.asList(tradeKey), tradeNo);
        if (flag.intValue()==0){
            throw  new RuntimeException("别重复提交订单,!尼龙啊");
        }
        //2.验证商品库存是否充足(远程调用第三方库存系统)
        //3.验证订单中商品价格是否发生变化(远程调用商品服务获取)
        // 获取订单中所有订单商品明细,,遍历商品,,,验证库存,,验证价格
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        //定义错误信息集合
        List<String> errorMsgList = new ArrayList<>();
        //定义异步任务集合
        ArrayList<CompletableFuture> allCompletableFutures = new ArrayList<>();
        if (!CollectionUtils.isEmpty(orderDetailList)){

            for (OrderDetail orderDetail : orderDetailList) {
                //远程调用库存系统,,看库存是否充足
                CompletableFuture<Void> stockCompletableFuture = CompletableFuture.runAsync(() -> {

                    String skuWareUrl = wareUrl + "/hasStock?skuId=" + orderDetail.getSkuId() + "&num=" + orderDetail.getSkuNum();
                    String doGet = HttpClientUtil.doGet(skuWareUrl);
                    if (!StringUtils.isNotBlank(doGet)){
                        errorMsgList.add("服务异常!!");
                    }
                    if (doGet.equals("0")) {
//                        throw new RuntimeException("商品库存不足");
                        errorMsgList.add("商品库存不足");
                    }
                }, threadPoolExecutor);
                allCompletableFutures.add(stockCompletableFuture);
                //远程调用商品微服务获取商品价格是否为最新
                CompletableFuture<Void> priceCompletableFuture = CompletableFuture.runAsync(() -> {

                    BigDecimal skuPrice = productFeignClient.getSkuPrice(orderDetail.getSkuId());
                    //比较,两个价格
                    if (skuPrice.compareTo(orderDetail.getOrderPrice()) != 0) {
                        //不相等,,,更新当前用户购物车中缓存价格更新为最新
                        String cartKey = RedisConst.USER_KEY_PREFIX + orderInfo.getUserId() + RedisConst.USER_CART_KEY_SUFFIX;
                        BoundHashOperations<String, String, CartInfo> hashOps = redisTemplate.boundHashOps(cartKey);
                        CartInfo cartInfo = hashOps.get(orderInfo.getUserId().toString());
                        if (cartInfo != null) {
                            //存入购物车中,,在放入缓存中
                            cartInfo.setSkuPrice(skuPrice);
                            hashOps.put(orderDetail.getSkuId().toString(), cartInfo);

                        }
//                        throw new RuntimeException("订单中商品价格失效!");
                        errorMsgList.add("订单中商品价格失效!");

                    }
                }, threadPoolExecutor);
                allCompletableFutures.add(priceCompletableFuture);
            }
            //将所有异步任务执行
            CompletableFuture.allOf(allCompletableFutures.toArray(new CompletableFuture[allCompletableFutures.size()])).join();
            //判断错误集合是否有数据
            if (!CollectionUtils.isEmpty(errorMsgList)){
                String errorMsg = errorMsgList.stream().collect(Collectors.joining(","));
                throw  new RuntimeException(errorMsg);
            }
        }
        //4.保存订单,保存订单明细
        Long orderId = saveOrderInfo(orderInfo);

        //5.发送延迟关闭订单消息到RabbitMQ 通知"订单微服务"验证订单支付状态   修改订单状态
        rabbitService.sendDelyMsg(MqConst.EXCHANGE_DIRECT_ORDER_CANCEL,MqConst.ROUTING_ORDER_CANCEL,orderId,300);

        return orderId;

    }

    /**
     * 生成流水号
     *
     * @param userId
     * @return
     */
    @Override
    public String generateTradeNo(String userId) {
        String tradeNo=UUID.randomUUID().toString().replaceAll("-", "");
        String tradeKey="tradeNo:"+userId;

        redisTemplate.opsForValue().set(tradeKey,tradeNo);

        return tradeNo;
    }

    /**
     * 验证流水号是否一致
     *
     * @param userId
     * @param tradeNo
     * @return
     */
    @Override
    public Boolean checkTradeNo(String userId, String tradeNo) {
        String tradeKey="tradeNo:"+userId;
        String  redisTradeNo = (String) redisTemplate.opsForValue().get(tradeKey);
        return tradeNo.equals(redisTradeNo);

    }

    /**
     * 删除业务流水号
     *
     * @param userId
     */
    @Override
    public void deleteTradeNo(String userId) {
        String tradeKey="tradeNo:"+userId;
        redisTemplate.delete(tradeKey);

    }

    /**
     * 查询我的订单列表
     *
     * @param infoPage
     * @param userId
     * @return
     */
    @Override
    public Page<OrderInfo> getOrderList(Page<OrderInfo> infoPage, String userId) {
        //方式1: 采用Wrapper 先查询当前用户所有订单 遍历订单 查询当前订单包含订单明细
        //方式2: 编写自定义SQL
        OrderInfoMapper orderInfoMapper = this.baseMapper;
        infoPage =  orderInfoMapper.getOrderList(infoPage,userId);
        return infoPage;
    }

    /**
     * 监听处理延迟关闭订单消息
     * @param orderId
     * @param channel
     * @param message
     */
    @Override
    public void execExpiredOrder(Long orderId) {
        this.updateOrderStatus(orderId,ProcessStatus.CLOSED);
    }

    /**
     * 根据订单ID查询订单信息 包含订单明细
     *
     * @param orderId
     * @return
     */
    @Override
    public OrderInfo getOrderInfoById(Long orderId) {
        //1.根据主键查询订单信息
        OrderInfo orderInfo = this.getById(orderId);
        if (orderInfo!=null){

            //2.根据订单ID 查询订单明细信息
            LambdaQueryWrapper<OrderDetail> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(OrderDetail::getOrderId,orderId);

            List<OrderDetail> orderDetailList = orderDetailService.list(queryWrapper);
            orderInfo.setOrderDetailList(orderDetailList);
            return orderInfo;
        }
        return null;
    }

    /**
     * 根据订单ID修改为指定订单状态
     * @param orderId
     * @param processStatus
     */

    public void updateOrderStatus(Long orderId, ProcessStatus processStatus) {
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setId(orderId);
        orderInfo.setProcessStatus(processStatus.name());
        orderInfo.setOrderStatus(processStatus.getOrderStatus().name());
        this.updateById(orderInfo);
    }

    /**
     * 保存订单,,方法提取出来了
     * @param orderInfo
     * @return
     */
    //既要保存订单,,还要保存订单明细,,所以要添加事务
    @Transactional(rollbackFor = Exception.class)
    public Long saveOrderInfo(OrderInfo orderInfo) {
        //1.保存订单信息
        //1.1为订单对象中部分属性赋值
        orderInfo.sumTotalAmount();//订单总金额
        orderInfo.setOrderStatus(OrderStatus.UNPAID.name());//订单状态
        //生成订单唯一编号,该编号用于支付  todo  可以采用雪花算法
        String outTradeNo="SPH"+System.currentTimeMillis()+ UUID.randomUUID().toString().replaceAll("-", "");
        orderInfo.setOutTradeNo(outTradeNo);
        //订单描述,,
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        if (!CollectionUtils.isEmpty(orderDetailList)){
            //获取图片
            orderInfo.setImgUrl(orderDetailList.get(0).getImgUrl());//默认订单现实的商品图片
            String tradeBody = orderDetailList.stream().map(OrderDetail::getSkuName).collect(Collectors.joining(","));
            if (tradeBody.length()>100){
                tradeBody.substring(0,100);
            }
            orderInfo.setTradeBody(tradeBody);
        }
        orderInfo.setOperateTime(new Date());
        //失效时间
        Calendar calendar = Calendar.getInstance();
        calendar.add(calendar.HOUR,1);
        orderInfo.setExpireTime(calendar.getTime());
        //订单处理状态,,详细,,给工作人员看的
        orderInfo.setProcessStatus(ProcessStatus.UNPAID.name());
        //省市区   todo 从用户收件地址中获取
        orderInfo.setProvinceId(1L);
        //保存订单
        this.save(orderInfo);
        Long orderId = orderInfo.getId();
        //2.保存订单明细信息
        orderDetailList.stream().forEach(orderDetail -> {
            orderDetail.setOrderId(orderId);
            //sourceid: 1:商城订单
            orderDetail.setSourceId(1L);
            orderDetail.setSourceType("MALL");

        });
        orderDetailService.saveBatch(orderDetailList);
        return orderId;
    }
}
